{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0bf70e6e",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "\n",
    "# Load the annotations CSV to check column names\n",
    "annotations = pd.read_csv('ConditionNames_SNOMED-CT.csv')\n",
    "print(annotations.columns)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "9beadf36",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import wfdb\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import tensorflow as tf\n",
    "from biosppy.signals import ecg as biosppy_ecg\n",
    "from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten, Dense\n",
    "from sklearn.model_selection import train_test_split\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "a719a527",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Function to parse .hea files and extract diagnosis codes\n",
    "def parse_header(file_path):\n",
    "    with open(file_path, 'r') as file:\n",
    "        lines = file.readlines()\n",
    "        for line in lines:\n",
    "            if line.startswith('#Dx'):\n",
    "                return line.split(': ')[1].strip().split(',')\n",
    "\n",
    "# Function to load and preprocess ECG data\n",
    "def load_ecg(file_path):\n",
    "    try:\n",
    "        record = wfdb.rdrecord(file_path)\n",
    "        ecg_signal = record.p_signal.flatten()\n",
    "        ecg_signal = (ecg_signal - np.mean(ecg_signal)) / np.std(ecg_signal)\n",
    "        return ecg_signal\n",
    "    except ValueError as e:\n",
    "        print(f\"ValueError reading {file_path}: {e}\")\n",
    "        return None\n",
    "    except IndexError as e:\n",
    "        print(f\"IndexError reading {file_path}: {e}\")\n",
    "        return None\n",
    "\n",
    "\n",
    "# Function to recursively search for .hea files\n",
    "def find_files(directory, extension):\n",
    "    for dirpath, dirnames, files in os.walk(directory):\n",
    "        for name in files:\n",
    "            if name.lower().endswith(extension):\n",
    "                yield os.path.join(dirpath, name)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "b55a9414",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'270492004': '1 degree atrioventricular block', '195042002': '2 degree atrioventricular block', '54016002': '2 degree atrioventricular block(Type one)', '28189009': '2 degree atrioventricular block(Type two)', '27885002': '3 degree atrioventricular block', '251173003': 'atrial bigeminy', '39732003': 'Axis left shift', '284470004': 'atrial\\xa0premature\\xa0beats', '164917005': 'abnormal Q wave', '47665007': 'Axis right shift', '233917008': 'atrioventricular block', '251199005': 'countercolockwise rotation', '251198002': 'colockwise rotation', '428417006': 'Early repolarization of the ventricles', '164942001': 'fQRS Wave', '698252002': 'Intraventricular block', '426995002': 'junctional escape beat', '251164006': 'junctional premature beat', '164909002': 'left front bundle branch block', '164873001': 'left ventricle hypertrophy', '251146004': 'lower voltage QRS in all lead', '251148003': 'lower voltage QRS in chest lead', '251147008': 'lower voltage QRS in limb lead', '164865005': 'Myocardial infraction in the side wall', '164947007': 'PR interval extension', '164912004': 'P wave Change', '111975006': 'QT interval extension', '446358003': 'right atrial hypertrophy', '59118001': 'right bundle branch block', '89792004': 'right ventricle hypertrophy', '429622005': 'ST drop down', '164930006': 'ST extension', '428750005': 'ST-T Change', '164931005': 'ST tilt up', '164934002': 'T wave Change', '59931005': 'T wave opposite', '164937009': 'U wave', '11157007': 'ventricular bigeminy', '75532003': 'ventricular escape beat', '13640000': 'ventricular fusion wave', '17338001': 'ventricular premature beat', '195060002': 'ventricular preexcitation', '251180001': 'ventricular escape trigeminy', '195101003': 'Sinus Atrium to Atrial Wandering Rhythm', '74390002': 'WPW', '426177001': 'Sinus Bradycardia', '426783006': 'Sinus Rhythm', '164889003': 'Atrial Fibrillation', '427084000': 'Sinus Tachycardia', '164890007': 'Atrial Flutter', '427393009': 'Sinus Irregularity', '426761007': 'Supraventricular Tachycardia', '713422000': 'Atrial Tachycardia', '233896004': 'Atrioventricular  Node Reentrant Tachycardia', '233897008': 'Atrioventricular Reentrant Tachycardia'}\n"
     ]
    }
   ],
   "source": [
    "# Path to the ECG records and the CSV file\n",
    "ecg_records_path = 'WFDBRecords'\n",
    "annotations_csv_path = 'ConditionNames_SNOMED-CT.csv'\n",
    "\n",
    "# Load the annotations CSV to create a mapping from SNOMED CT codes to condition names\n",
    "annotations = pd.read_csv(annotations_csv_path)\n",
    "code_to_condition_map = dict(zip(annotations['Snomed_CT'].astype(str), annotations['Full Name']))\n",
    "\n",
    "# Print out the mapping to verify\n",
    "print(code_to_condition_map)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "9bc9379f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Skipping file due to error in heart rate computation: WFDBRecords/35/354/JS34868, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/35/354/JS34879, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/35/353/JS34788, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/35/350/JS34479, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/35/357/JS35192, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/35/356/JS35050, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/35/356/JS35065, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/35/351/JS34509, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/33/338/JS33280, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/20/203/JS19708, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/20/209/JS20330, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/27/274/JS26843, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/27/273/JS26793, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/27/272/JS26605, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/27/275/JS26977, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/27/277/JS27170, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/27/278/JS27278, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/27/278/JS27271, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/27/276/JS27034, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/11/115/JS10951, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/11/114/JS10890, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/11/113/JS10767, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/11/113/JS10765, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/29/292/JS28648, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/29/293/JS28757, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/16/168/JS16222, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/16/167/JS16169, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/16/162/JS15624, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/42/425/JS41908, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/42/425/JS41935, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/42/424/JS41844, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/42/423/JS41772, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/42/426/JS42026, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/42/429/JS42330, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/45/451/JS44571, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/28/280/JS27460, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/28/280/JS27407, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/28/286/JS28075, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/28/284/JS27835, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/28/285/JS27985, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/17/174/JS16899, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/17/174/JS16813, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/10/108/JS10260, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/19/199/JS19309, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/26/268/JS26245, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/26/266/JS26009, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/26/260/JS25458, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/26/267/JS26145, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/26/267/JS26130, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/21/217/JS21144, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/21/212/JS20656, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/44/440/JS43400, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/44/441/JS43569, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/44/443/JS43710, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/43/439/JS43330, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/43/430/JS42415, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/43/430/JS42400, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/43/432/JS42631, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/43/432/JS42614, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/43/432/JS42676, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/38/383/JS37781, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/38/382/JS37609, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/38/380/JS37439, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/38/381/JS37592, Error: Not enough beats to compute heart rate.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Skipping file due to error in heart rate computation: WFDBRecords/38/388/JS38231, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/38/388/JS38252, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/36/362/JS35654, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/36/363/JS35727, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/36/364/JS35857, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/36/366/JS36015, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/36/366/JS36018, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/36/368/JS36244, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/36/367/JS36189, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/37/377/JS37105, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/37/377/JS37173, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/37/377/JS37176, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/37/371/JS36568, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/37/373/JS36731, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/01/010/JS00057, Error: Not enough beats to compute heart rate.\n",
      "ValueError reading WFDBRecords/01/019/JS01052: time data '/' does not match format '%d/%m/%Y'\n",
      "Skipping file due to loading error: WFDBRecords/01/019/JS01052\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/24/245/JS23950, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/24/243/JS23786, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/24/241/JS23588, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/24/246/JS24016, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/24/240/JS23482, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/24/240/JS23450, Error: Not enough beats to compute heart rate.\n",
      "IndexError reading WFDBRecords/23/236/JS23074: list index out of range\n",
      "Skipping file due to loading error: WFDBRecords/23/236/JS23074\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/23/237/JS23116, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/15/152/JS14659, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/15/152/JS14627, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/12/124/JS11897, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/12/124/JS11887, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/12/125/JS11956, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/13/133/JS12751, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/13/137/JS13181, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/14/147/JS14161, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/14/149/JS14343, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/22/223/JS21701, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/22/224/JS21881, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/22/224/JS21853, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/22/222/JS21668, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/22/222/JS21617, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/25/257/JS25106, Error: Not enough beats to compute heart rate.\n",
      "Skipping file due to error in heart rate computation: WFDBRecords/25/259/JS25322, Error: Not enough beats to compute heart rate.\n",
      "Loaded 45048 ECG records.\n",
      "Number of normal ECG records: 673\n",
      "Number of abnormal ECG records: 44375\n"
     ]
    }
   ],
   "source": [
    "# Initialize data and labels lists\n",
    "ecg_data = []\n",
    "labels = []\n",
    "\n",
    "# Load each ECG record and assign labels\n",
    "for file_path in find_files(ecg_records_path, '.hea'):\n",
    "    base_path = file_path.replace('.hea', '')\n",
    "    mat_file_path = base_path + '.mat'\n",
    "    hea_file_path = base_path + '.hea'\n",
    "\n",
    "    if not os.path.isfile(mat_file_path) or not os.path.isfile(hea_file_path):\n",
    "        print(f\"Missing file for {base_path}\")\n",
    "        continue\n",
    "    \n",
    "    diagnosis_codes = parse_header(file_path)\n",
    "    label = 1 if any(code_to_condition_map.get(code, '') != '' for code in diagnosis_codes) else 0\n",
    "    ecg_signal_prefiltered = load_ecg(base_path)\n",
    "    if ecg_signal_prefiltered is not None:\n",
    "        try:\n",
    "            # Attempt to denoise ECG signal using BioSPPy\n",
    "            output = biosppy_ecg.ecg(signal=ecg_signal_prefiltered, sampling_rate=500., show=False)\n",
    "            ecg_signal = output['filtered']  # Accessing the filtered ECG signal\n",
    "            ecg_data.append(ecg_signal)\n",
    "            labels.append(label)\n",
    "        except ValueError as e:\n",
    "            print(f\"Skipping file due to error in heart rate computation: {base_path}, Error: {e}\")\n",
    "    else:\n",
    "        print(f\"Skipping file due to loading error: {base_path}\")\n",
    "\n",
    "# Check if any data was loaded\n",
    "if not ecg_data:\n",
    "    print(\"No ECG data was loaded.\")\n",
    "else:\n",
    "    print(f\"Loaded {len(ecg_data)} ECG records.\")\n",
    "    # Count the number of normal (0) and abnormal (1) labels\n",
    "    abnormal_count = sum(labels)\n",
    "    normal_count = len(labels) - abnormal_count\n",
    "    print(f\"Number of normal ECG records: {normal_count}\")\n",
    "    print(f\"Number of abnormal ECG records: {abnormal_count}\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "a8cead41",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ECG data shape: (45048, 60000, 1)\n",
      "Labels: [1 1 1 ... 1 1 1]\n"
     ]
    }
   ],
   "source": [
    "# Convert to NumPy arrays and add channel dimension for Conv1D input\n",
    "if ecg_data:\n",
    "    ecg_data = np.array(ecg_data)[..., np.newaxis]\n",
    "    labels = np.array(labels)\n",
    "    print(f\"ECG data shape: {ecg_data.shape}\")\n",
    "    print(f\"Labels: {labels}\")\n",
    "else:\n",
    "    # Handle the case where no data is loaded\n",
    "    print(\"No data to preprocess.\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "093299d7",
   "metadata": {},
   "outputs": [],
   "source": [
    "def create_model(input_shape):\n",
    "    # Define the model architecture\n",
    "    model = tf.keras.Sequential([\n",
    "        Conv1D(16, kernel_size=3, activation='relu', input_shape=input_shape),\n",
    "        MaxPooling1D(2),\n",
    "        Flatten(),\n",
    "        Dense(50, activation='relu'),\n",
    "        Dense(1, activation='sigmoid')\n",
    "    ])\n",
    "    # Create the optimizer with gradient clipping\n",
    "    optimizer = tf.keras.optimizers.Adam(learning_rate=0.001, clipvalue=1.0)\n",
    "    # Compile the model with the optimizer\n",
    "    model.compile(optimizer=optimizer, loss='binary_crossentropy', metrics=['accuracy'])\n",
    "    return model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "e89a2f5e",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/kjingruz/anaconda3/lib/python3.11/site-packages/keras/src/layers/convolutional/base_conv.py:99: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n",
      "  super().__init__(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/10\n",
      "\u001b[1m901/901\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m89s\u001b[0m 97ms/step - accuracy: 0.9739 - loss: 0.3809 - val_accuracy: 0.9868 - val_loss: 0.0734\n",
      "Epoch 2/10\n",
      "\u001b[1m901/901\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m73s\u001b[0m 80ms/step - accuracy: 0.9851 - loss: 0.0570 - val_accuracy: 0.9865 - val_loss: 0.0620\n",
      "Epoch 3/10\n",
      "\u001b[1m901/901\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m74s\u001b[0m 82ms/step - accuracy: 0.9879 - loss: 0.0372 - val_accuracy: 0.9849 - val_loss: 0.0666\n",
      "Epoch 4/10\n",
      "\u001b[1m901/901\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m73s\u001b[0m 81ms/step - accuracy: 0.9887 - loss: 0.0298 - val_accuracy: 0.9846 - val_loss: 0.0931\n",
      "Epoch 5/10\n",
      "\u001b[1m901/901\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m73s\u001b[0m 81ms/step - accuracy: 0.9929 - loss: 0.0162 - val_accuracy: 0.9849 - val_loss: 0.0880\n",
      "Epoch 6/10\n",
      "\u001b[1m901/901\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m73s\u001b[0m 81ms/step - accuracy: 0.9940 - loss: 0.0137 - val_accuracy: 0.9858 - val_loss: 0.1315\n",
      "Epoch 7/10\n",
      "\u001b[1m901/901\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m74s\u001b[0m 82ms/step - accuracy: 0.9935 - loss: 0.0134 - val_accuracy: 0.9852 - val_loss: 0.1255\n",
      "Epoch 8/10\n",
      "\u001b[1m901/901\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m75s\u001b[0m 83ms/step - accuracy: 0.9972 - loss: 0.0081 - val_accuracy: 0.9843 - val_loss: 0.1687\n",
      "Epoch 9/10\n",
      "\u001b[1m901/901\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m73s\u001b[0m 81ms/step - accuracy: 0.9984 - loss: 0.0056 - val_accuracy: 0.9838 - val_loss: 0.1644\n",
      "Epoch 10/10\n",
      "\u001b[1m901/901\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m72s\u001b[0m 80ms/step - accuracy: 0.9979 - loss: 0.0067 - val_accuracy: 0.9814 - val_loss: 0.1725\n"
     ]
    }
   ],
   "source": [
    "if len(ecg_data) > 0:\n",
    "    # Split data into train and test sets\n",
    "    X_train, X_test, y_train, y_test = train_test_split(ecg_data, labels, test_size=0.2, random_state=42)\n",
    "    \n",
    "    # Check if there is enough data\n",
    "    if X_train.shape[0] > 0:\n",
    "        # Create and compile the model\n",
    "        model = create_model((X_train.shape[1], 1))\n",
    "        # Train the model\n",
    "        history = model.fit(X_train, y_train, epochs=10, validation_split=0.2)\n",
    "    else:\n",
    "        print(\"Not enough data to create training and test sets.\")\n",
    "else:\n",
    "    print(\"No data to train the model.\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "eb341ca4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1m282/282\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m6s\u001b[0m 20ms/step - accuracy: 0.9793 - loss: 0.2483\n",
      "Test Accuracy: 97.97%\n"
     ]
    }
   ],
   "source": [
    "# Evaluate the model only if it was trained\n",
    "if 'history' in locals():\n",
    "    loss, accuracy = model.evaluate(X_test, y_test)\n",
    "    print(f'Test Accuracy: {accuracy * 100:.2f}%')\n",
    "else:\n",
    "    print(\"Model has not been trained.\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "53707dfe",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Save the entire model as a SavedModel with a `.keras` extension\n",
    "model.save('ECG_Trained.keras')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c86ad8f6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Load the model back\n",
    "loaded_model = load_model('ECG_Trained.keras')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8129c750",
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QLabel, QFileDialog\n",
    "from tensorflow.keras.models import load_model\n",
    "import numpy as np\n",
    "import wfdb\n",
    "\n",
    "class ECGApp(QWidget):\n",
    "    def __init__(self):\n",
    "        super().__init__()\n",
    "        self.initUI()\n",
    "\n",
    "    def initUI(self):\n",
    "        self.setWindowTitle('ECG Signal Classification')\n",
    "        self.setGeometry(100, 100, 800, 600)\n",
    "\n",
    "        # Layout\n",
    "        layout = QVBoxLayout()\n",
    "        \n",
    "        # Label\n",
    "        self.label = QLabel('Upload an ECG .mat file to classify')\n",
    "        layout.addWidget(self.label)\n",
    "        \n",
    "        # Button\n",
    "        self.button = QPushButton('Load ECG File', self)\n",
    "        self.button.clicked.connect(self.loadECG)\n",
    "        layout.addWidget(self.button)\n",
    "        \n",
    "        # Set the layout\n",
    "        self.setLayout(layout)\n",
    "\n",
    "    def loadECG(self):\n",
    "        options = QFileDialog.Options()\n",
    "        fileName, _ = QFileDialog.getOpenFileName(self, \"Load ECG\", \"\", \"MAT Files (*.mat);;All Files (*)\", options=options)\n",
    "        if fileName:\n",
    "            self.classifyECG(fileName)\n",
    "\n",
    "    def classifyECG(self, file_path):\n",
    "        # Load the signal\n",
    "        record = wfdb.rdrecord(file_path[:-4])\n",
    "        signal = record.p_signal.flatten()\n",
    "        signal = (signal - np.mean(signal)) / np.std(signal)\n",
    "        \n",
    "        # Preprocess and reshape the signal for the model\n",
    "        signal = signal.reshape((1, -1, 1))  # Adjust based on your model's expected input\n",
    "        \n",
    "        # Load model and predict\n",
    "        model = load_model('my_model.h5')\n",
    "        prediction = model.predict(signal)\n",
    "        label = 'Abnormal' if prediction > 0.5 else 'Normal'\n",
    "        \n",
    "        # Update UI\n",
    "        self.label.setText(f'The ECG is classified as: {label}')\n",
    "\n",
    "# Start the application\n",
    "def main():\n",
    "    app = QApplication(sys.argv)\n",
    "    ex = ECGApp()\n",
    "    ex.show()\n",
    "    sys.exit(app.exec_())\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    main()\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
